﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WPFShapeDrawing
{
    class Triangle
    {
        private double[] angles;
        private double[] sides;
        private int flagForAngles = 0;
        private int flagForSides = 0;

        public Triangle(double[] angles, double[] sides)
        {
            this.angles = angles;
            this.sides = sides;
        }

        public void setFlags()
        {
            for (int i = 0; i < angles.Length; i++)
            {
                if (angles[i] != 0)
                    flagForAngles++;
                if (sides[i] != 0)
                    flagForSides++;
            }
        }

        public int getUseCase()
        {
            setFlags();
            if (flagForAngles >= 2 && flagForSides == 0) return 1;
            else if (flagForAngles >= 2 && flagForSides == 1) return 2;
            else if (flagForAngles >= 1 && flagForSides == 2) return 3;
            else if (flagForAngles >= 0 && flagForSides == 3) return 4;
            else return 5;

        }

        double ToRadians(double Angle)
        {
            return Angle * Math.PI / 180;
        }

        double ToDegrees(double Angle)
        {
            return Angle * 180 / Math.PI;
        }

        public double[] Angles
        {
            get { return angles; }
            set { angles = value; }
        }
        public double[] Sides
        {
            get { return sides; }
            set { sides = value; }
        }

        private Triangle Case1()
        {
            //deep copy of angles
            double[] angles = new double[3];
            double[] sides = new double[3];

            for (int i = 0; i < 3; i++)
            {
                angles[i] = this.angles[i];
                sides[i] = this.sides[i];
            } 

            if (flagForAngles == 3)
            {
                if ((angles.Sum()) != 180)
                {
                    //MessageBox.Show("Error");
                    // Return error saying that the sum of the angles should be equal to 180. It means that the sum actually
                    // is less than or greater than 180.
                    throw new System.InvalidOperationException("The sum of the three angle together need to be 180°");
                    //return null;
                }
            }
            else
            {
                for (int i = 0; i < 3; i++)
                {
                    if (angles[i] == 0)
                    {
                        angles[i] = 180 - angles[(i + 1) % 3] - angles[(i + 2) % 3];
                        if (angles[i] < 0) throw new System.InvalidOperationException("The sum of the three angle together need to be 180°");
                    }
                }
            }

            //Find the maximum angle
            int k = 0;
            for (int i = 1; i < 3; i++)
            {
                if (angles[k] < angles[i])
                    k = i;
            }

            //Set the longest side 250 (250 is x here)
            sides[k] = 1;

            //Find other sides
            for (int i = 0; i < 3; i++)
            {
                if (sides[i] == 0)
                    sides[i] = sides[k] * Math.Sin(ToRadians(angles[i])) / Math.Sin(ToRadians(angles[k]));
            }

            return new Triangle(angles, sides);
        }
        //This is the case when we have 1 side and 2 or 3 angles.
        //Assuem empty input is 0.
        //When a user input the second angles, check the sum of angles is smaller than 180,
        //and if so, calcualate the other angle.
        //Therefore, we do not need to check the angles here
        private Triangle Case2()
        {
            //deep copy of angles and sides
            double[] angles = new double[3];
            double[] sides = new double[3];

            for (int i = 0; i < 3; i++)
            {
                angles[i] = this.angles[i];
                sides[i] = this.sides[i];
            } 

            if (flagForAngles == 3)
            {
                if ((angles.Sum()) != 180)
                {
                    //MessageBox.Show("Error");
                    // Return error saying that the sum of the angles should be equal to 180. It means that the sum actually
                    // is less than or greater than 180.
                    throw new System.InvalidOperationException("The sum of the three angle together need to be 180°");
                    //return null;
                }
            }
            else
            {
                for (int i = 0; i < 3; i++)
                    if (angles[i] == 0)
                    {
                        angles[i] = 180 - angles[(i + 1) % 3] - angles[(i + 2) % 3];
                        if (angles[i] < 0) throw new System.InvalidOperationException("The sum of the three angle together need to be 180°");
                    }
            }

            // Get k of existing side
            int k = 0;
            for (int i = 0; i < 3; i++)
            {
                if (sides[i] != 0)
                    k = i;
            }

            //Find other sides
            for (int i = 0; i < 3; i++)
            {
                if (sides[i] == 0)
                    sides[i] = sides[k] * Math.Sin(ToRadians(angles[i])) / Math.Sin(ToRadians(angles[k]));
            }

            return new Triangle(angles, sides);
    }

        //Case3 is the scenario that we get 2 sides and 1 or more angles.
        //Assume that the values of non-inputed angles are 0.
        private Triangle Case3()
        {
            double[] angles = new double[3];
            double[] sides = new double[3];

            for (int i = 0; i < 3; i++)
            {
                angles[i] = this.angles[i];
                sides[i] = this.sides[i];
            }

            if (flagForAngles == 1)
            {
                // Check if the opposite angle of the unknown side is known
                for (int i = 0; i < 3; i++)
                {
                    if (sides[i] == 0 && angles[i] != 0)
                    {
                        sides[i] = Math.Sqrt(Math.Pow(sides[(i + 1) % 3], 2.0) + Math.Pow(sides[(i + 2) % 3], 2.0) - 2 * sides[(i + 1) % 3] * sides[(i + 2) % 3] * Math.Cos(ToRadians(angles[i])));
                    }
                    // But if there is an angle and side that are opposite of each other, it can be solved instantly
                    else if (sides[i] != 0 && angles[i] != 0)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            if (sides[j] != 0 && angles[j] == 0 && j != i)
                            {
                                angles[j] = ToDegrees(Math.Asin(sides[j] * Math.Sin(ToRadians(angles[i])) / sides[i]));
                                angles[3 - (i + j)] = 180 - angles[i] - angles[j];
                                sides[3 - (i + j)] = sides[i] * Math.Sin(ToRadians(angles[3 - (i + j)])) / Math.Sin(ToRadians(angles[i]));
                            }
                        }
                    }
                }

                //Find other angles
                for (int i = 0; i < 3; i++)
                {
                    if (angles[i] == 0)
                        angles[i] = ToDegrees(Math.Acos((Math.Pow(sides[(i + 1) % 3], 2.0) + Math.Pow(sides[(i + 2) % 3], 2.0) - Math.Pow(sides[i], 2.0)) / (2 * sides[(i + 1) % 3] * sides[(i + 2) % 3])));
                }
            }
            else if (flagForAngles >= 2)
            {

                if (flagForAngles == 2)
                {

                    // Complete with the last angle
                    for (int i = 0; i < 3; i++)
                        if (angles[i] == 0)
                        {
                            angles[i] = 180 - angles[(i + 1) % 3] - angles[(i + 2) % 3];
                            if (angles[i] < 0) throw new System.InvalidOperationException("The sum of the three angle together need to be 180°");
                        }

                }
                else
                {
                    if ((angles.Sum()) != 180)
                    {
                        //MessageBox.Show("Error");
                        // Return error saying that the sum of the angles should be equal to 180. It means that the sum actually
                        // is less than or greater than 180.
                        throw new System.InvalidOperationException("The sum of the three angle together need to be 180°");
                        //return null;
                    }
                }

                double[] temp_sides = new double[3];
                double difference;

                for (int i = 0; i < 3; i++)
                {
                    temp_sides[i] = sides[i];
                }

                // Define the temporary side unknown
                for (int i = 0; i < 3; i++)
                {
                    if (temp_sides[i] == 0)
                        temp_sides[i] = Math.Sqrt(Math.Pow(temp_sides[(i + 1) % 3], 2.0) + Math.Pow(temp_sides[(i + 2) % 3], 2.0) - 2 * temp_sides[(i + 1) % 3] * temp_sides[(i + 2) % 3] * Math.Cos(ToRadians(angles[i])));
                }

                // Define all the sides
                for (int i = 0; i < 3; i++)
                {
                    temp_sides[i] = Math.Sqrt(Math.Pow(temp_sides[(i + 1) % 3], 2.0) + Math.Pow(temp_sides[(i + 2) % 3], 2.0) - 2 * temp_sides[(i + 1) % 3] * temp_sides[(i + 2) % 3] * Math.Cos(ToRadians(angles[i])));
                }

                // Compare the measurements of the sides in order to realize if the inputs are correct.
                for (int i = 0; i < 3; i++)
                {
                    difference = Math.Abs(sides[i] * .00001);
                    if (sides[i] != 0 && Math.Abs(sides[i] - temp_sides[i]) > difference)
                    {
                        throw new System.InvalidOperationException("Unsolvable triangle. Incorrect sides or angles were given.");
                    }
                    else
                    {
                        sides[i] = temp_sides[i];
                    }
                }
           
            }

            return new Triangle(angles, sides);

        }
        //If error happens, return null
        private Triangle Case4()
        {
            
            double[] angles = new double[3];
            double[] sides = new double[3];
            double[] temp_angles = new double[3];

            for (int i = 0; i < 3; i++)
            {
                angles[i] = this.angles[i];
                sides[i] = this.sides[i];
            }

            for (int i = 0; i < 3; i++)
            {
                temp_angles[i] = ToDegrees(Math.Acos((Math.Pow(sides[(i + 1) % 3], 2.0) + Math.Pow(sides[(i + 2) % 3], 2.0) - Math.Pow(sides[i], 2.0)) / (2 * sides[(i + 1) % 3] * sides[(i + 2) % 3])));
                if (Double.IsNaN(temp_angles[i]))
                {
                    throw new System.InvalidOperationException("Unsolvable triangle. Incorrect sides or angles were given.");
                }
            }

            double difference;

            // Compare the measurements of the angles in order to realize if the inputs are correct.
            for (int i = 0; i < 3; i++)
            {
                difference = Math.Abs(angles[i] * .00001);
                if (angles[i] != 0 && Math.Abs(angles[i] - temp_angles[i]) > difference)
                {
                    throw new System.InvalidOperationException("Unsolvable triangle. Incorrect sides or angles were given.");
                }
                else
                {
                    angles[i] = temp_angles[i];
                }
            }

            return new Triangle(angles, sides);
        }
        private Triangle Case5()
        {
            throw new System.InvalidOperationException("Unsolvable triangle. Insufficient information to solve the triangle.");
        }
        public Triangle solve()
        {
            switch (this.getUseCase())
            {
                case 1: return Case1();
                case 2: return Case2();
                case 3: return Case3();
                case 4: return Case4();
                case 5: return Case5();
                //default:
                //throw new Triangle.ArgumentEx
            }
            return null;
        }

    }

}
